// <com.woyouqiu.Copyright>
// --------------------------------------------------------------
// <copyright>上海有求网络科技有限公司 2015</copyright>
// <Solution>MyCompany.MyProject</Solution>
// <Project>MyCompany.MyProject.Application</Project>
// <FileName>ProductAppService.cs</FileName>
// <CreateTime>2017-04-21 17:58</CreateTime>
// <Author>何苗</Author>
// <Email>hemiao@woyouqiu.com</Email>
// <log date="2017-04-21 17:58" version="00001">创建</log>
// --------------------------------------------------------------
// </com.woyouqiu.Copyright>

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Threading.Tasks;
using Abp.Application.Services;
using Abp.Application.Services.Dto;
using Abp.Authorization;
using Abp.AutoMapper;
using Abp.Domain.Repositories;
using Abp.Linq.Extensions;
using Abp.UI;
using Newtonsoft.Json;
using Vecen.BangChuang.BangChuang.Areas;
using Vecen.BangChuang.BangChuang.Areas.DomainDtos;
using Vecen.BangChuang.BangChuang.Common;
using Vecen.BangChuang.BangChuang.Common.Dtos;
using Vecen.BangChuang.BangChuang.DomainServices.Common;
using Vecen.BangChuang.BangChuang.DomainServices.Products;
using Vecen.BangChuang.BangChuang.Mall;
using Vecen.BangChuang.BangChuang.Products.Dto;

namespace Vecen.BangChuang.BangChuang.Products {
    /// <summary>
    /// 商品操作
    /// </summary>
    [AbpAuthorize]
    public class ProductAppService: BangChuangAppServiceBase, IProductAppService {
        private readonly IRepository<Product> _productRepository;
        private readonly IRepository<ProductCategory> _productCategoryRepository;
        private readonly ProductDomainService _productDomainService;
        private readonly IRepository<Favorite> _favoriteRepository;
        private readonly CityDomainService _cityDomainService;
        private readonly CounterDomainService _counterDomainService;

        /// <summary>
        /// </summary>
        /// <param name="productRepository"></param>
        /// <param name="productCategoryRepository"></param>
        /// <param name="productDomainService"></param>
        /// <param name="favoriteRepository"></param>
        /// <param name="cityDomainService"></param>
        /// <param name="counterDomainService"></param>
        public ProductAppService(IRepository<Product> productRepository,
            IRepository<ProductCategory> productCategoryRepository,
            ProductDomainService productDomainService,
            IRepository<Favorite> favoriteRepository,
            CityDomainService cityDomainService,
            CounterDomainService counterDomainService) {
            _productRepository = productRepository;
            _productCategoryRepository = productCategoryRepository;
            _productDomainService = productDomainService;
            _favoriteRepository = favoriteRepository;
            _cityDomainService = cityDomainService;
            _counterDomainService = counterDomainService;
        }

        /// <summary>
        /// 获得单个商品信息
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [AbpAllowAnonymous]
        public async Task<ProductDetailOutDto> GetProductAsync(int id) {
            var entity = await _productRepository.FirstOrDefaultAsync(id);
            var dto = entity.MapTo<ProductDetailOutDto>();
            dto.Price = entity.ExFactoryPrice;
            dto.HeaderImageIds = await AttachmentManager.GetIdsAsync(Product.ProductHeaderAttachmentType, id);
            dto.Address = entity.AreaCode.HasValue
                ? (await _cityDomainService.GetFullAddressByCityId(entity.AreaCode.Value)).MapTo<FullCityOutDto>()
                : null;
            try {
                var dictionary = JsonConvert.DeserializeObject<Dictionary<string, string>>(entity.Parameters);
                dto.Parameters = dictionary;
            } catch(Exception exception) {
                Logger.DebugFormat(exception, "商品{0}参数内容不标准!-{1}", entity.Name, entity.Parameters);
            }
            return dto;
        }

        /// <summary>
        /// 创建商品
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<int> CreateOrUpdateProductAsync(ProductInputDto input) {
            var entity = input.MapTo<Product>();
            if(input.CategoryId.HasValue) {
                var category = await _productCategoryRepository.FirstOrDefaultAsync(input.CategoryId.Value);
                if(category == null) {
                    throw new UserFriendlyException("请求失败!", "商品分类不存在或已被删除!");
                }
                entity.CategoruFullCode = category.FullCode;
            }
            entity.AreaCode = _cityDomainService.GetAreaId(input);
            entity.Status = ProductStatus.不可售;
            var id = await _productRepository.InsertOrUpdateAndGetIdAsync(entity);
            await AttachmentManager.UpdateObjectIdAsync(Product.ProductHeaderAttachmentType, input.AttachmentIds, id);
            await AttachmentManager.DelAttachmentAsync(Product.ProductHeaderAttachmentType, input.DelAttachmentIds);
            return id;
        }


        /// <summary>
        /// 删除商品
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task DeleteProductAsync(int id) {
            await _productDomainService.DeleteProductAsync(id);
        }

        /// <summary>
        /// 下架
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task OffTheShelfAsync(EntityDto input) {
            await _productDomainService.OffTheShelfAsync(input.Id);
        }

        /// <summary>
        /// 上架
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task OnTheShelfAsync(EntityDto input) {
            await _productDomainService.OnTheShelfAsync(input.Id);
        }

        /// <summary>
        /// 增加库存
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task AddToInventoryAsync(AddToInventoryInputDto input) {
            await _counterDomainService.RecordAsync(CounterType.Product.Inventory, input.Id, input.AddToInventoryCount);
        }

        /// <summary>
        /// 获得待编辑商品内容
        /// </summary>
        /// <param name="id">商品ID</param>
        /// <returns></returns>
        public async Task<ProductInputDto> GetProductForEditAsync(int id) {
            var product = await _productRepository.FirstOrDefaultAsync(id);
            ProductInputDto dto = new ProductInputDto();
            ProductType productType;
            if(Enum.TryParse(product.ProductType, true, out productType)) {
                dto.ProductType = productType;
            }
            if(product.AreaCode.HasValue) {
                var cityInfo = await _cityDomainService.GetFullAddressByCityId(product.AreaCode.Value);
                dto.ProvinceId = cityInfo.ProvinceId;
                dto.CityId = cityInfo.CityId;
                dto.DistrictId = cityInfo.DistrictId;
            }
            dto.CategoryId = product.CategoryId;
            dto.Name = product.Name;
            dto.Description = product.Description;
            dto.GraphicContent = product.GraphicContent;
            dto.AgentPrice = product.AgentPrice;
            dto.ExFactoryPrice = product.ExFactoryPrice;
            dto.CostPrice = product.CostPrice;
            dto.Id = product.Id;
            dto.AttachmentIds = await AttachmentManager.GetIdsAsync(Product.ProductHeaderAttachmentType, id);
            return dto;
        }

        /// <summary>
        /// 后台获取商品列表
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<PagedResultDto<ProductListForAdminOutDto>> GetProductsForAdminAsync(
            GetProductsForAdminInputDto input) {
            var query = await _productDomainService.GetProductsByKeyword(input.Name);
            query = query.Where(c => c.ProductType == input.ProductType.ToString());
            ProductCategory category = null;
            if(input.CategoryId.HasValue) {
                category = await _productCategoryRepository.FirstOrDefaultAsync(input.CategoryId.Value);
            }
            query = query.WhereIf(category != null, c => c.CategoruFullCode.StartsWith(category.FullCode));

            var total = query.Count();
            var entities = query.OrderBy(input.Sorting).PageBy(input).ToList();
            var dtos = await EntityMap2Dto(entities);
            var result = new List<ProductListForAdminOutDto>();
            foreach(var dto in dtos) {
                var resultDto = dto.MapTo<ProductListForAdminOutDto>();
                var product = entities.FirstOrDefault(c => c.Id == dto.Id);
                resultDto.ClickCount = await _counterDomainService.GetCountAsync(CounterType.Product.Click, dto.Id);
                resultDto.ForwardedCount =
                    await _counterDomainService.GetCountAsync(CounterType.Product.Forwarded, dto.Id);
                resultDto.SaledCount = await _counterDomainService.GetCountAsync(CounterType.Product.Saled, dto.Id);
                resultDto.TotalCount =
                    await _counterDomainService.GetSumCountAsync(CounterType.Product.Inventory, dto.Id);
                resultDto.AgentPrice = product.AgentPrice;
                resultDto.ExFactoryPrice = product.ExFactoryPrice;
                resultDto.CostPrice = product.CostPrice;
                resultDto.Status = entities.Find(c => c.Id == dto.Id).Status;
                result.Add(resultDto);
            }

            return new PagedResultDto<ProductListForAdminOutDto>(total, result);
        }

        /// <summary>
        /// 获得商品列表
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        [AbpAllowAnonymous]
        public async Task<PagedResultDto<ProductListOutDto>> GetProductsAsync(GetProductsRequestDto input) {
            var query = await _productDomainService.GetProductsByKeyword(input.Name);
            query = query.Where(c => c.ProductType == input.ProductType.ToString());
            ProductCategory category = null;
            if(input.CategoryId.HasValue && input.ProductType == ProductType.Jewelry) {
                category = await _productCategoryRepository.FirstOrDefaultAsync(input.CategoryId.Value);
            }
            query = query.WhereIf(category != null, c => c.CategoruFullCode.StartsWith(category.FullCode));

            var total = query.Count();
            var entities = query.OrderBy(input.Sorting).PageBy(input).ToList();

            return new PagedResultDto<ProductListOutDto>(total, await EntityMap2Dto(entities));
        }

        /// <summary>
        /// 获得今日新品
        /// </summary>
        /// <returns></returns>
        [AbpAllowAnonymous]
        public async Task<PagedResultDto<ProductListOutDto>> GetProductsForTodayAsync(GetProductsRequestDto input) {
            var query = await _productDomainService.GetProductsByKeyword(input.Name);
            query = query.Where(c => c.ProductType == input.ProductType.ToString());
            ProductCategory category = null;
            if(input.CategoryId.HasValue && input.ProductType == ProductType.Jewelry) {
                category = await _productCategoryRepository.FirstOrDefaultAsync(input.CategoryId.Value);
            }
            query = query.WhereIf(category != null, c => c.CategoruFullCode.StartsWith(category.FullCode))
                .Where(c => c.LastModificationTime.HasValue && c.LastModificationTime.Value < DateTime.Now.AddDays(-1)
                            || c.CreationTime < DateTime.Now.AddDays(-1));
            var total = query.Count();
            var entities = query.OrderByDescending(c => c.CreationTime).PageBy(input).ToList();
            return new PagedResultDto<ProductListOutDto>(total, await EntityMap2Dto(entities));
        }


        private async Task<List<ProductListOutDto>> EntityMap2Dto(List<Product> products) {
            var dtos = new List<ProductListOutDto>();
            foreach(var product in products) {
                var dto = product.MapTo<ProductListOutDto>();
                dto.HeaderImageId = await AttachmentManager.GetIdAsync(Product.ProductThumbnailAttachmentType, dto.Id);
                if(!dto.HeaderImageId.HasValue) {
                    dto.HeaderImageId = await AttachmentManager.GetIdAsync(Product.ProductHeaderAttachmentType, dto.Id);
                }
                dto.IsFavorite =
                    await _favoriteRepository.FirstOrDefaultAsync(c => c.FavoriteType == dto.ProductType.ToString()
                                                                       && c.ObjectId == dto.Id && c.CreatorUserId
                                                                       == AbpSession.UserId) != null;

                dto.Price = product.ExFactoryPrice;
                dto.Category = (await _productCategoryRepository.FirstOrDefaultAsync(c => c.Id == product.CategoryId))
                    ?.Name;
                dto.Address = await _cityDomainService.GetFullAddressDtoByCityId(product.AreaCode ?? 0);
                dtos.Add(dto);
            }
            return dtos;
        }

        /// <summary>
        /// 获得广告商品列表
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        [AbpAllowAnonymous]
        [Obsolete("转到推广信息接口")]
        public async Task<PagedResultDto<ProductListOutDto>> GetProductsForAdAsync(GetProductsRequestDto input) {
            var query = await _productDomainService.GetProductsByKeyword(input.Name);
            query = query.Where(c => c.ProductType == input.ProductType.ToString()).Where(c => c.TopNumber != null);
            ProductCategory category = null;
            if(input.CategoryId.HasValue) {
                category = await _productCategoryRepository.FirstOrDefaultAsync(input.CategoryId.Value);
            }
            query = query.WhereIf(category != null, c => c.CategoruFullCode.StartsWith(category.FullCode))
                .Where(c => c.TopNumber.HasValue);
            var total = query.Count();
            var entities = query.OrderByDescending(c => c.TopNumber).PageBy(input).ToList();
            return new PagedResultDto<ProductListOutDto>(total, await EntityMap2Dto(entities));
        }

        /// <summary>
        /// 设置广告商品权重
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        [Obsolete("转到推广信息接口")]
        public async Task SetTopNumberAsync(SetTopNumberInputDto input) {
            var product = await _productRepository.FirstOrDefaultAsync(input.Id);
            if(product != null) {
                product.TopNumber = input.TopNumber;
            }

            await _productRepository.UpdateAsync(product);
        }

        /// <summary>
        /// 获得分享内容
        /// </summary>
        /// <param name="productId"></param>
        /// <returns></returns>
        public async Task<ShareModel> GetShareModelByIdAsync(int productId) {
            var product = await _productRepository.FirstOrDefaultAsync(productId);
            if(product == null) {
                throw new UserFriendlyException("获得分享信息出错!", "商品信息已被删除!");
            }
            var shareModel = new ShareModel();
            shareModel.Description = product.Description;
            shareModel.ImgUrl = RequestInfo.HostBaseUri + "/file/download/"
                                + (await AttachmentManager.GetIdAsync(Product.ProductHeaderAttachmentType, productId));
            shareModel.Title = product.Name;
            shareModel.Link = RequestInfo.HostBaseUri + "/micro/product?id=" + productId + "";
            return shareModel;
        }

        /// <summary>
        /// 分享成功回调
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task ForwardedSuccessCallback(ShareSuccessInputDto input) {
            await _counterDomainService.RecordForwardedAsync(CounterType.Product.Forwarded, input.ObjectId,
                input.ShareType);
        }

        /// <summary>
        /// 商品点击量记录
        /// </summary>
        /// <param name="productId"></param>
        /// <returns></returns>
        [RemoteService(false)]
        public async Task ClickSuccessCallback(int productId) {
            await _counterDomainService.RecordAsync(CounterType.Product.Click, productId);
        }
    }
}